﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography.X509Certificates;
using System.IO;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Math;
using System.Xml;
using System.Security.Cryptography;
using Youz.Common.Helper;
using Youz.CreditRepay.Entity;

namespace Youz.CreditRepay.BLL.Pay
{
    public class PPayHelper
    {
        public const String MERCHANTID = "701705271516233";

        public const String KEY = "R1rOEgDoE8vGc+fesJyIvSjqRovZ+dGG4ni0anHORmo=";

        public const String PayUrl = "http://113.240.245.133:81/ppayGate/payRelatedInterface/doBusiness.html";

        /// <summary>
        /// 支付回调URL
        /// </summary>
        public const String PayCallbackUrl = "http://192.168.1.109:8001/ppay/PayCallback.ashx";
        /// <summary>
        /// 代付回调URL
        /// </summary>
        public const String ReceiptCallbackUrl = "http://192.168.1.109:8001/ppay/ReceiptCallback.ashx";

        public const String VERSION = "2.0.0";

        private const String privateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALHxfVnADIl8LZPlZM/9EcTpgcH7WBj4QUp/ElzDKAEG8YzpMQdWYRFHN+M83pNlj6bhfl/tP86+e1iaG6Hhxb8rLK6l6kGxfZNmWKmpIWIYU4iE4f+ISuWcogvTdqr2u3fkymLEbkyJTQsHSCsbEP/A1uLUQomPcgGMyCcc6DdzAgMBAAECgYB7jPE8H9yUmWQMDHqy70QnNPqjdaD1tWmgEBrvzZE5JVkSPdeNAKzlEk37uYfRT/MuK+NTN6fWJOv/2QDWpaxmphMmKM/OAOy6grFwELGRSSS7Iq+6pgMznd6LWjTj2OrETgJN24WkHAx6DlsI6qHLZv0vOIUSNOu4HqxNWznyAQJBAOrqG47YRUeTl8sjira2IO3jXc03H/JbD7N1miaUQzuxrULO+M6Oh+mlVQbQRujtmvZDXR/cbultiT+6I8u5WGcCQQDB6kq91TAq7wyfR+sWPEGOOtq+RHPOelAk1z0l3Ke+MlBBFvtBYE9qdBk59WuLjU5rHPBW2Z8u2braWhjgzfEVAkAh05YmSpQnS2R3NuX5i15QgwX1z/NEtcIcCForJfxMczhMv3rqW3HMvibxNTqQve4reeXF+brq2AAEcixHYKgPAkEAsrx47ebuAfTbRolsp88cgbO/4faIY74b1wlJxHdRah9IF+bN6qGSMcYXZnPPEA9MQ7Cl2ve2IOHxt8ep/LP2TQJAUOkbTWy9vgPudsL+wbeYu6szxnNenw6sCSoxtVYgXmpzUM6KuvIvoJh5Z0kgTnNfkooRuZW5ICrejEs2AJFO0Q==";
        private const String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCx8X1ZwAyJfC2T5WTP/RHE6YHB+1gY+EFKfxJcwygBBvGM6TEHVmERRzfjPN6TZY+m4X5f7T/OvntYmhuh4cW/KyyupepBsX2TZlipqSFiGFOIhOH/iErlnKIL03aq9rt35MpixG5MiU0LB0grGxD/wNbi1EKJj3IBjMgnHOg3cwIDAQAB";

        private String pingKey = null;
        private String workKey = null;
        private String mobKey = null;

        private Pay_PPayResultsBLL bllPayResults = new Pay_PPayResultsBLL();

        public PPayHelper()
        {
            pingKey = RandHelper.Str(24);
            workKey = RandHelper.Str(24);
            mobKey = RSAEncrypt(pingKey + workKey);
        }

        /// <summary>
        /// 支付
        /// </summary>
        /// <returns></returns>
        public string Pay(string memberAccount, string id, decimal amount, int inCardID, int outCardID, PayTradeType tradetype)
        {
            CR_BankCardBLL bllBankCard = new CR_BankCardBLL();
            Member_AccountBLL bllMemberAccount = new Member_AccountBLL();
            CR_RepaymentPlanBLL bllRepaymentPlan = new CR_RepaymentPlanBLL();
            Member_RechargeBLL bllRecharge = new Member_RechargeBLL();
            Member_WithdrawBLL bllWithdraw = new Member_WithdrawBLL();
            Pay_RecordsBLL bllPayRecords = new Pay_RecordsBLL();
            var tradeNo = string.Empty;
            decimal payAmount = 0, payFee = 0, totalPay = 0;

            if (string.IsNullOrEmpty(id) || amount == 0 || inCardID == 0)
            {
                return Core.AjaxResult(false, "支付失败，参数错误");
            }

            switch (tradetype)
            {
                case PayTradeType.Repay:
                    #region 还款
                    var plan = bllRepaymentPlan.Get(TypeHelper.ConvertTo<int>(id));
                    if (plan == null)
                    {
                        return Core.AjaxResult(false, "支付失败，参数错误");
                    }
                    payAmount = plan.RepaymentAmount;
                    payFee = plan.Fee;
                    break;
                    #endregion
                case PayTradeType.Recharge:
                    #region 充值
                    var recharge = bllRecharge.Get(id);
                    if (recharge == null)
                    {
                        return Core.AjaxResult(false, "支付失败，参数错误");
                    }
                    payAmount = recharge.FromAmount;
                    payFee = recharge.Fee;
                    break;
                    #endregion
                case PayTradeType.Withdraw:
                    var withdraw = bllWithdraw.Get(id);
                    payAmount = withdraw.WithdrawAmount;
                    payFee = withdraw.WithdrawFee;
                    break;
            }

            totalPay = payAmount + payFee;
            if (totalPay != amount)
            {
                return Core.AjaxResult(false, "支付失败，支付金额错误");
            }

            var result = false;
            switch (inCardID)
            {
                case -2://新卡
                    break;
                case -1://余额支付
                    #region 余额支付
                    var asset = bllMemberAccount.Get(memberAccount);
                    if (asset.Balance < totalPay)
                    {
                        return Core.AjaxResult(false, "支付失败，余额不足");
                    }
                    tradeNo = bllPayRecords.Add(PaymentType.Balance, tradetype, memberAccount, totalPay, id);
                    if (string.IsNullOrEmpty(tradeNo))
                    {
                        return Core.AjaxResult(false, "支付失败，系统错误");
                    }

                    result = bllMemberAccount.BalancePay(memberAccount, payAmount, payFee, tradeNo); 
                    #endregion
                    break;
                default://银行卡
                    #region 银行卡
                    var card = bllBankCard.Get(inCardID);
                    if (card == null)
                    {
                        return Core.AjaxResult(false, "支付失败，银行卡错误");
                    }
                    tradeNo = bllPayRecords.Add(PaymentType.BankCard, tradetype, memberAccount, totalPay, id);
                    if (string.IsNullOrEmpty(tradeNo))
                    {
                        return Core.AjaxResult(false, "支付失败，系统错误");
                    }

                    Dictionary<String, String> resqData = new Dictionary<string, string>();


                    //付款银行 加密字段
                    resqData.Add("MerchOrderId", tradeNo); //商户订单号

                    if (tradetype != PayTradeType.Withdraw)
                    {
                        resqData.Add("Amount", totalPay.ToString()); //支付金额
                        resqData.Add("MobileNo", card.BankMobile); //手机号码
                        resqData.Add("Name", card.Name); //客户姓名
                        resqData.Add("CertNo", card.IDNo); //身份证号码
                        resqData.Add("BankAccount", card.CardNo); //银行卡号
                        resqData.Add("Pwd", card.Password); //银行卡密码 3、	银行卡密码： 123456  4、	信用卡CVN： 随意填写3位数字
                    }

                    if (tradetype == PayTradeType.Getcash)
                    {
                        var outCard = bllBankCard.Get(outCardID);
                        if (outCard == null)
                        {
                            return Core.AjaxResult(false, "支付失败，银行卡错误");
                        }

                        //收款银行
                        resqData.Add("AccNo", outCard.CardNo); //收款人卡号
                        resqData.Add("AccName", outCard.Name); //收款人姓名
                        resqData.Add("SettleAmt", payAmount.ToString()); //付款金额
                    }
                    else if(tradetype == PayTradeType.Withdraw)
                    {
                        //收款银行
                        resqData.Add("AccNo", card.CardNo); //收款人卡号
                        resqData.Add("AccName", card.Name); //收款人姓名
                        resqData.Add("SettleAmt", payAmount.ToString()); //付款金额
                    }

                    result = Pay(tradeNo, resqData); 
                    #endregion
                    break;
            }

            result = true;
            if (result)
            {
                object returnObj = null;
                switch (tradetype)
                {
                    case PayTradeType.Repay:
                        bllRepaymentPlan.UpdateStatus(TypeHelper.ConvertTo<int>(id), RepaymentPlanStatus.Runing);
                        returnObj = bllPayRecords.Get(tradeNo);
                        break;
                    case PayTradeType.Recharge:
                        bllRecharge.UpdateRechargeStatus(id, RechargeStatus.Auditing);
                        returnObj = bllPayRecords.Get(tradeNo);
                        break;
                    case PayTradeType.Withdraw:
                        bllWithdraw.Update(id, WithdrawStatus.Submited);
                        returnObj = bllWithdraw.Get(id);
                        break;
                }
                if (inCardID > 0)
                {
                    bllPayRecords.UpdateStatus(tradeNo, PayRecordsStatus.Paying);
                }
                return Core.AjaxResult(true, "", returnObj);
            }
            else
            {
                bllPayRecords.UpdateStatus(tradeNo, PayRecordsStatus.Failure);
                return Core.AjaxResult(false, "支付失败，系统错误", bllPayRecords.Get(tradeNo));
            }
        }

        private bool Pay(string orderID, Dictionary<String, String> resqData)
        {
            try
            {
                //新增支付记录表
                var payResults = new Pay_PPayResults();
                payResults.MerchOrderId = orderID;
                payResults.TradeCode = "PayQuickByAccPwd";
                payResults.Version = PPayHelper.VERSION;
                payResults.MerchantId = PPayHelper.MERCHANTID;
                if (!new Pay_PPayResultsBLL().Add(payResults))
                {
                    return false;
                }

                //组装参数
                Dictionary<String, String> dictParams = BuildPayData(resqData);
                String json = "reqJson=" + Core.GetJsonByObject(dictParams);

                //请求支付
                var body = WebRequestHelper.GetResponseTextPOST(PayUrl, json);
                var obj = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<String, String>>(body);
                var result = obj["RetCode"] == "0000";
                
                //更新支付记录
                if (obj.ContainsKey("OrderId"))
                {
                    payResults.OrderId = obj["OrderId"];
                }
                payResults.RetCode = obj["RetCode"];
                payResults.RetMsg = obj["RetMsg"];
                payResults.PayTime = null;
                payResults.SettleDate = null;

                if (!bllPayResults.Update(payResults))
                {
                    Core.WriteOperationLog("PPayHelper", "Pay", "UpdatePayResults", false, "更新PayResults失败");
                    return false;
                }

                return result;
            }
            catch (Exception ex)
            {
                Core.WriteErrorLog(ex);
                return false;
            }
        }

        private Dictionary<String, String> BuildPayData(Dictionary<String, String> bankData)
        {
            Dictionary<String, String> resqData = new Dictionary<string, string>();
            resqData.Add("TradeCode", "PayQuickByAccPwd");
            resqData.Add("Version", VERSION);
            resqData.Add("MobKey", mobKey);

            resqData.Add("MerchantId", Encrypt3Des(MERCHANTID));  //商户号
            resqData.Add("BgUrl", PayCallbackUrl); //商户回调地址

            //以下是D0必填项
            resqData.Add("SettleType", "1"); //清算类型 0(t+1)清算 / 1(D0)清算
            resqData.Add("IsSettlement", "0"); //1不清算  0自动清算

            //付款银行 加密字段
            resqData.Add("MerchOrderId", bankData["MerchOrderId"]); //商户订单号
            resqData.Add("Amount", Encrypt3Des(bankData["Amount"])); //支付金额
            resqData.Add("MobileNo", Encrypt3Des(bankData["MobileNo"])); //手机号码
            resqData.Add("Name", Encrypt3Des(bankData["Name"])); //客户姓名
            resqData.Add("CertNo", Encrypt3Des(bankData["CertNo"])); //身份证号码
            resqData.Add("BankAccount", Encrypt3Des(bankData["BankAccount"])); //银行卡号
            resqData.Add("Pwd", Encrypt3Des(bankData["Pwd"])); //银行卡密码 3、	银行卡密码： 123456  4、	信用卡CVN： 随意填写3位数字

            //收款银行
            resqData.Add("AccNo", bankData["AccNo"]); //收款人卡号
            resqData.Add("AccName", bankData["AccName"]); //收款人姓名
            resqData.Add("SettleAmt", bankData["SettleAmt"]); //付款金额

            //以下是D0选填项，为提高代付成功率，建议用户填写
            resqData.Add("AccProvince", ""); //开户行省份
            resqData.Add("AccCity", ""); //开户行城市
            resqData.Add("AccbankName", ""); //开户支行名称
            resqData.Add("AccType", "0");// 0私人 1公司 不填默认私人
            resqData.Add("NotifyUrl", ReceiptCallbackUrl);//代付异步通知地址


            //签名  签名参数： 具体字段参考文档
            //TradeCode,MobKey
            String[] signNames = { "TradeCode", "MobKey" };
            resqData.Add("sign", GetReqSign(signNames, resqData));

            return resqData;
        }

        #region RSA加解密
        /// <summary>
        /// RSA解密
        /// </summary>
        /// <param name="privateKey"></param>
        /// <param name="strEncryptString"></param>
        /// <returns></returns>
        public string RSADecrypt(string privateKey, string strEncryptString)
        {
            RsaPrivateCrtKeyParameters privateKeyParam = (RsaPrivateCrtKeyParameters)PrivateKeyFactory.CreateKey(Convert.FromBase64String(privateKey));

            //参数与Java中加密解密的参数一致
            IBufferedCipher c = CipherUtilities.GetCipher("RSA");

            //第一个参数 true-加密，false-解密；第二个参数表示密钥
            c.Init(false, privateKeyParam);

            //对密文进行base64解码
            byte[] dataFromEncrypt = Convert.FromBase64String(strEncryptString);

            //解密
            byte[] outBytes = c.DoFinal(dataFromEncrypt);

            //明文
            string clearText = Encoding.Default.GetString(outBytes);

            return clearText;
        }

        public string RSADecrypt(string encryptString)
        {
            return RSADecrypt(privateKey, encryptString);
        }

        /// <summary>
        /// RSA加密
        /// </summary>
        /// <param name="publicKey"></param>
        /// <param name="str"></param>
        /// <returns></returns>
        public string RSAEncrypt(string publicKey, string str)
        {
            RsaKeyParameters key = (RsaKeyParameters)PublicKeyFactory.CreateKey(Convert.FromBase64String(publicKey));

            //参数与Java中加密解密的参数一致
            IBufferedCipher c = CipherUtilities.GetCipher("RSA");

            //第一个参数 true-加密，false-解密；第二个参数表示密钥
            c.Init(true, key);

            byte[] dataBytes = Encoding.Default.GetBytes(str);

            //加密
            byte[] outBytes = c.DoFinal(dataBytes);

            return Convert.ToBase64String(outBytes);
        }

        public string RSAEncrypt(string str)
        {
            return RSAEncrypt(publicKey, str);
        }
        #endregion

        #region 3des加解密
        public string Encrypt3Des(string str)
        {
            return Encrypt3Des(str, pingKey);
        }

        /// <summary>
        /// 3des ecb模式加密
        /// </summary>
        /// <param name="aStrString">待加密的字符串</param>
        /// <param name="aStrKey">密钥</param>
        /// <param name="iv">加密矢量：只有在CBC解密模式下才适用</param>
        /// <param name="mode">运算模式</param>
        /// <returns>加密后的字符串</returns>
        public string Encrypt3Des(string aStrString, string aStrKey, CipherMode mode = CipherMode.CBC, string iv = "01234567")
        {
            var des = new TripleDESCryptoServiceProvider
            {
                Key = Encoding.UTF8.GetBytes(aStrKey),
                Mode = mode
            };
            if (mode == CipherMode.CBC)
            {
                des.IV = Encoding.UTF8.GetBytes(iv);
            }
            var desEncrypt = des.CreateEncryptor();
            byte[] buffer = Encoding.UTF8.GetBytes(aStrString);
            return Convert.ToBase64String(desEncrypt.TransformFinalBlock(buffer, 0, buffer.Length));

        }

        /// <summary>
        /// des 解密
        /// </summary>
        /// <param name="aStrString">加密的字符串</param>
        /// <param name="aStrKey">密钥</param>
        /// <param name="iv">解密矢量：只有在CBC解密模式下才适用</param>
        /// <param name="mode">运算模式</param>
        /// <returns>解密的字符串</returns>
        public string Decrypt3Des(string aStrString, string aStrKey, CipherMode mode = CipherMode.CBC, string iv = "01234567")
        {
            var des = new TripleDESCryptoServiceProvider
            {
                Key = Encoding.UTF8.GetBytes(aStrKey),
                Mode = mode,
                Padding = PaddingMode.PKCS7
            };
            if (mode == CipherMode.CBC)
            {
                des.IV = Encoding.UTF8.GetBytes(iv);
            }
            var desDecrypt = des.CreateDecryptor();
            var result = "";
            byte[] buffer = Convert.FromBase64String(aStrString);
            result = Encoding.UTF8.GetString(desDecrypt.TransformFinalBlock(buffer, 0, buffer.Length));
            return result;
        }
        #endregion

        /// <summary>
        /// 32位MD5加密
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public string MD532Encrypt(string str)
        {
            var md5Csp = new MD5CryptoServiceProvider();
            byte[] md5Source = Encoding.UTF8.GetBytes(str);
            byte[] md5Out = md5Csp.ComputeHash(md5Source);
            string pwd = "";
            for (int i = 0; i < md5Out.Length; i++)
            {
                pwd += md5Out[i].ToString("x2");
            }
            return pwd;
        }

        /// <summary>
        /// 签名
        /// </summary>
        /// <param name="paramNames"></param>
        /// <param name="map"></param>
        /// <returns></returns>
        public string GetReqSign(String[] paramNames, Dictionary<String, String> map)
        {
            String sign = "";
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < paramNames.Length; i++)
            {
                String v = map[paramNames[i].Trim()];
                if (null != v)
                {
                    sb.Append(v);
                }
            }
            sign = Encrypt3Des(MD532Encrypt(sb.ToString()), workKey);
            return sign;
        }
    }
}
